﻿using System;
using System.Linq;
using CrmClient.Mapping;
using Microsoft.Xrm.Sdk;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Xrm.Sdk.Client;
using System.Threading.Tasks;

namespace CrmClient.QueryConvert
{
    internal class DefaultQueryConvertor<T> : QueryConvertorBase<T>
    {
        private static PropertiesMap GetDefaultPropertiesMap()
        {
            var res = new PropertiesMap();
            foreach (var property in typeof(T).GetProperties())
                if (property.IsDefined(typeof(AttributeLogicalNameAttribute), true))
                    res.Add(new PropertyMap(MappingUtils.GetCrmAttributeName(property, null), property.Name, property.PropertyType));
            return res;
        }
        private static string GetDefaultEntityName()
        {
            return MappingUtils.GetCrmEntityName(typeof(T));
        }

        public DefaultQueryConvertor(PropertiesMap propertiesMap, string entityName)
            : base(propertiesMap, entityName)
        { }

        public DefaultQueryConvertor()
            : base(GetDefaultPropertiesMap(), GetDefaultEntityName())
        { }

        public override Entity CreateEntity(T item, Guid? id)
        {
            var res = new Entity(EntityName);
            if (id.HasValue)
                res.Id = id.Value;
            foreach (var propertyMap in PropertiesMap.Where(p => p.IsMappedToCrmEntity && !p.CrmAttr.IsId))
                res.Attributes.Add(propertyMap.CrmAttr.ToString(), GetValue(propertyMap, item));
            return res;
        }

        protected override T CreateItemWithConstructor(Entity entity)
        {
            var constructorData = new object[PropertiesMap.Count];
            for (int i = 0; i < constructorData.Length; i++)
                constructorData[i] = GetValue(PropertiesMap[i], entity);
            return (T)TypeConstructor.Invoke(constructorData);
        }

        /// <summary>
        /// Медленный, из-за reflection
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected override T CreateItemAndFill(Entity entity)
        {
            T res = Activator.CreateInstance<T>();
            foreach (var propertyMap in PropertiesMap)
                Type.GetProperty(propertyMap.ClassPropName).SetValue(res, GetValue(propertyMap, entity), null);
            return res;
        }

        protected virtual object GetValue(PropertyMap propertyMap, Entity entity)
        {
            object val = null;
            if (propertyMap.IsMappedToCrmEntity)
            {
                var attrName = propertyMap.CrmAttr.ToString();
                if (entity.Attributes.ContainsKey(attrName))
                {
                    if (propertyMap.CrmAttr.IsAliased)
                        val = ((AliasedValue)entity.Attributes[attrName]).Value;
                    else
                        val = entity.Attributes[attrName];
                }
            }
            else
                val = propertyMap.ConstantValue;
            return val;
        }

        /// <summary>
        /// Медленный, из-за reflection
        /// </summary>
        /// <param name="propertyMap"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected virtual object GetValue(PropertyMap propertyMap, T entity)
        {
            return Type.GetProperty(propertyMap.ClassPropName).GetValue(entity, null);
        }
    }
}
